package br.com.virtual.recomendar.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import br.com.virtual.cadastrar.model.Pessoa;
import br.com.virtual.cadastrar.model.Produto;
import br.com.virtual.recomendar.model.Avaliacao;
import br.com.virtual.recomendar.service.LocalizadorProdutosRecomendar;

public class LocalizadorProdutosRecomendarImpl implements LocalizadorProdutosRecomendar {

	private Map<Produto, Float> somaAvaliacoes;
	private Map<Produto, Float> somaSemelhanca;
	@SuppressWarnings("unused")
	private List<Produto> produtosIgnorar;
	
	private Map<Pessoa, Float> semelhantes;

	public LocalizadorProdutosRecomendarImpl(Map<Pessoa, Float> semelhantes) {
		this.semelhantes = semelhantes;
		this.produtosIgnorar = new ArrayList<Produto>();
		init();
	}

	public LocalizadorProdutosRecomendarImpl(Map<Pessoa, Float> semelhantes, List<Produto> produtosIgnorar) {
		this.semelhantes = semelhantes;
		this.produtosIgnorar = produtosIgnorar;
		
		init();
	}
	
	
	public List<Produto> gerarRecomendacoes() {

		List<Produto> recomendacoes = null;
		
		init();
		Set<Pessoa> pessoasSemelhantes = semelhantes.keySet();
		
		for (Pessoa pessoaSemelhante : pessoasSemelhantes) {
			
			multiplicaAvaliacaoSemelhanca(semelhantes.get(pessoaSemelhante), pessoaSemelhante);
		}
		
		Map<Produto, Float> avaliacaoMediaProduto = calcularAvaliacaoMediaProdutos();
		
		recomendacoes = ordenaRecomendacoes(avaliacaoMediaProduto);
		
		
//		Map<Produto, Float> mediaOrdenada = 
//				new TreeMap<Produto, Float>(new ProdutoAvaliadoComparator(avaliacaoMediaProduto));
//		mediaOrdenada.putAll(avaliacaoMediaProduto);
//		recomendacoes = new ArrayList<Produto>( mediaOrdenada.keySet());
		
		
		return recomendacoes;
		
	}
	
	private List<Produto> ordenaRecomendacoes(
			Map<Produto, Float> avaliacaoMediaProduto) {

		Map<Float, List<Produto>> mediaOrdenada = new TreeMap<Float, List<Produto>>();
		List<Produto> retornar = new ArrayList<Produto>();
		
		List<Produto> produtos = new ArrayList<Produto>(avaliacaoMediaProduto.keySet());
		
		for (Produto produto : produtos) {
			
			Float media = avaliacaoMediaProduto.get(produto);
//			System.out.print(media + " <-- ");
			
			if( mediaOrdenada.containsKey(media))
				mediaOrdenada.get(media).add(produto);
			else{
				List<Produto> produtosAdd = new ArrayList<Produto>();
				produtosAdd.add(produto);
				mediaOrdenada.put(media, produtosAdd);
			}
		}
		
		List<Float> mediasEncontradas = new ArrayList<Float>( mediaOrdenada.keySet());
		Collections.sort(mediasEncontradas, Collections.reverseOrder());
		
		for(Float media : mediasEncontradas){
//			System.out.println(media + " <-- ");
			retornar.addAll(mediaOrdenada.get(media));
		}
		
		
		return retornar;
	}

	private Map<Produto, Float> calcularAvaliacaoMediaProdutos(){
		
		Map<Produto, Float> produtosAvaliacao = new TreeMap<Produto, Float>();
		
		Set<Produto>produtos = this.somaAvaliacoes.keySet();
		
		for (Produto produto : produtos) {
			
			Float mediaAvaliacao = this.somaAvaliacoes.get(produto) / this.somaSemelhanca.get(produto);
			produtosAvaliacao.put(produto, mediaAvaliacao);
			
		}
		
		return produtosAvaliacao;
	}

	private void multiplicaAvaliacaoSemelhanca(Float valorSemelhanca, Pessoa pessoa) {
		
		List<Avaliacao> avaliacoesDoSemelhante = pessoa.getAvaliacoes();
		Avaliacao avaliacaoAux;

		for (Avaliacao avaliacao : avaliacoesDoSemelhante) {
			
			avaliacaoAux = new Avaliacao(avaliacao.getProduto(), avaliacao.getPessoa(), avaliacao.getNota() * valorSemelhanca);
			
//			avaliacao.setNota(avaliacao.getNota() * valorSemelhanca);
			
//			if( !produtosIgnorar.contains(avaliacao.getProduto())){
				somarAvaliacao(avaliacaoAux.getProduto(), avaliacao.getNota());
				somarSemelhanca(avaliacaoAux.getProduto(), valorSemelhanca);
//			}
		}

	}

	
	private void somarAvaliacao(Produto produto, Float avaliacao){
		
		if( this.somaAvaliacoes.containsKey(produto))
			this.somaAvaliacoes.put(produto, this.somaAvaliacoes.get(produto) + avaliacao);
		else
			this.somaAvaliacoes.put(produto, avaliacao);
	}
	
	private void somarSemelhanca(Produto produto, Float semelhanca){
		
		if( this.somaSemelhanca.containsKey(produto))
			this.somaSemelhanca.put(produto, this.somaSemelhanca.get(produto)+semelhanca);
		else
			this.somaSemelhanca.put(produto, semelhanca);
	}
	
	private void init(){
		
		this.somaAvaliacoes = new TreeMap<Produto, Float>();
		this.somaSemelhanca = new TreeMap<Produto, Float>();
	}

}